/*
Copyright (c) 2009, David A. Freels Sr.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that
the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

package net.whippetcode.plugins.intellij.hudson;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager;
import net.whippetcode.plugins.intellij.hudson.ui.HudsonDetailPanel;
import net.whippetcode.plugins.intellij.hudson.ui.Job;
import net.whippetcode.plugins.intellij.hudson.ui.Status;
import net.whippetcode.plugins.intellij.hudson.ui.StatusIndicator;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimerTask;

/**
 * This class is responsible for calling the Hudson XML API and parsing the results.
 * 
 * @author David A. Freels Sr.
 */
class ServerPollingTask extends TimerTask
{
    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
	private static final XPathFactory factory = XPathFactory.newInstance();
	private static final XPath xpath = factory.newXPath();

	private Map<String, Status> jobs = new HashMap<String, Status>();
	private URL url;
	private StatusIndicator statusIndicator;
	private Project project;
	private static final String DISABLED = "disabled";
    private DefaultMutableTreeNode root;
    private HudsonDetailPanel hudsonDetailPanel;

    ServerPollingTask(HudsonMonitorSettings settings, StatusIndicator statusIndicator, HudsonDetailPanel hudsonDetailPanel, Project project) throws URISyntaxException, MalformedURLException
	{
		this.statusIndicator = statusIndicator;
        this.hudsonDetailPanel = hudsonDetailPanel;
        this.root = new DefaultMutableTreeNode("Jobs");
		this.project = project;
		URI uri = new URI(settings.getServerAddress() + (settings.isUseView() ? "/view/" + settings.getViewName().replaceAll(" ", "%20") : "") + "/api/xml?depth=2");
		url = uri.toURL();
	}

	public void run()
	{
		StatusBar sb = WindowManager.getInstance().getStatusBar(project);
		try
		{
			InputStream stream = url.openStream();

			Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream);

			NodeList jobs = document.getElementsByTagName("job");
			Node job;
			int length = jobs.getLength();
			String jobName;
			String color;
			StringBuilder statusMessage = new StringBuilder("<html><body><table border=0>");
			Status totalStatus = Status.BLUE;
            DefaultMutableTreeNode jobNode;
            boolean running;
            String inQueue;
            String lastSucessfulBuild;
            String lastFailedBuild;
            String lastStableBuild;
            String timestamp;
            Job jobBean;
            Status status;
			for (int i = 0; i < length; i++)
			{
                running = false;
				job = jobs.item(i);

				jobName = xpath.evaluate("child::name", job);
				color = xpath.evaluate("child::color", job);
                inQueue = xpath.evaluate("child::inQueue", job);
                lastSucessfulBuild = xpath.evaluate("child::lastSuccessfulBuild/child::number", job);
                lastFailedBuild = xpath.evaluate("child::lastFailedBuild/child::number", job);
                lastStableBuild = xpath.evaluate("child::lastStableBuild/child::number", job);

				//Get to the actual color when a build is in progress.
				if (color.indexOf("_") != -1)
				{
                    running = true;
					color = color.substring(0, color.indexOf("_"));
				}

				if ("grey".equalsIgnoreCase(color) || "aborted".equalsIgnoreCase(color))
				{
					color = DISABLED;
				}

                status = Status.valueOf(color.toUpperCase());

                jobBean = new Job(jobName, status, running, "true".equalsIgnoreCase(inQueue));
                jobNode = new DefaultMutableTreeNode(jobBean);

                this.root.add(jobNode);

                timestamp = xpath.evaluate("child::lastSuccessfulBuild/child::timestamp", job);
                jobNode.add(new DefaultMutableTreeNode("Last Successful Build (" + lastSucessfulBuild + ") : " + (timestamp != null && timestamp.trim().length() > 0 ? DATE_FORMAT.format(new Date(Long.parseLong(timestamp))) : "")));
                timestamp = xpath.evaluate("child::lastFailedBuild/child::timestamp", job);
                jobNode.add(new DefaultMutableTreeNode("Last Failed Build (" + lastFailedBuild + ") : " + (timestamp != null && timestamp.trim().length() > 0 ? DATE_FORMAT.format(new Date(Long.parseLong(timestamp))) : "")));
                timestamp = xpath.evaluate("child::lastStableBuild/child::timestamp", job);
                jobNode.add(new DefaultMutableTreeNode("Last Stable Build (" + lastStableBuild + ") : " + (timestamp != null && timestamp.trim().length() > 0 ? DATE_FORMAT.format(new Date(Long.parseLong(timestamp))) : "")));

                statusMessage.append("<tr><td>").append(jobName).append("</td><td bgcolor=").append(color.equals(DISABLED) ? "gray" : color ).append("></td></tr>");

				if (totalStatus.compareTo(status) == 1)
				{
					totalStatus = status;
				}

				if (!this.jobs.containsKey(jobName))
				{

					this.jobs.put(jobName, status);
				}
			}
			statusMessage.append("</table></body></html>");
			statusIndicator.updateTooltip(statusMessage.toString());
			statusIndicator.updateStatus(totalStatus);
            hudsonDetailPanel.setJobList(this.root);

			stream.close();
		}
		catch (MalformedURLException e)
		{
			statusIndicator.updateTooltip("Invalid URL provided: " + url.toString());
			statusIndicator.updateStatus(Status.DISABLED);
		}
		catch (IOException e)
		{
			statusIndicator.updateTooltip("Could not connect to " + url.toString());
			statusIndicator.updateStatus(Status.DISABLED);
		}
		catch (SAXException e)
		{
			//Do nothing
			sb.setInfo("Error parsing XML");
		}
		catch (ParserConfigurationException e)
		{
			//Do nothing
			sb.setInfo("Error parsing XML");
		}
		catch (Exception e)
		{
			sb.setInfo(e.getMessage());
		}
	}
}
